package com.pktechan.manage.utils;

//import com.alibaba.fastjson.JSONObject;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.pktechan.manage.exception.CustomException;
import com.pktechan.manage.exception.RemoteServiceException;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * HTTP 请求工具
 *
 * @Description:
 * @Author: Wang Jiahao
 * @Date: on 2016/8/1 9:20
 */
public class HttpUtil {
    private static Logger log = LoggerFactory.getLogger(HttpUtil.class);

    private static PoolingHttpClientConnectionManager connMgr;
    private static RequestConfig requestConfig;
    //超时时间
    private static final int CONNECT_TIMEOUT = 3000;
    private static final int SOCKET_TIMEOUT = 10000;
    private static final int CONNECTION_REQUEST_TIMEOUT = 2000;

    private static final String SSL_HTTP_PREFIX = "https";


    static {
        // 设置连接池
        connMgr = new PoolingHttpClientConnectionManager();
        // 设置连接池大小
        connMgr.setMaxTotal(100);
        connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());

        RequestConfig.Builder configBuilder = RequestConfig.custom();
        // 设置连接超时
        configBuilder.setConnectTimeout(CONNECT_TIMEOUT);
        // 设置读取超时
        configBuilder.setSocketTimeout(SOCKET_TIMEOUT);
        // 设置从连接池获取连接实例的超时
        configBuilder.setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT);
        // 在提交请求之前 测试连接是否可用
        configBuilder.setStaleConnectionCheckEnabled(true);
        requestConfig = configBuilder.build();
    }


    /**
     * 发送 GET 请求（HTTP），不带输入数据
     *
     * @param url
     * @return
     */
    public synchronized static String doGet(String url) throws Exception {
        return doGet(url, new HashMap<String, Object>());
    }

    /**
     * 发送 GET 请求（HTTP），不带输入数据
     *
     * @param url
     * @return
     */
    public synchronized static <T> ResponseMessage<T> doGetForResponseMessage(String url, TypeReference typeReference) throws Exception {
        return doGetForResponseMessage(url, new HashMap<String, Object>(), typeReference);
    }

    /**
     * 发送 GET 请求（HTTP），K-V形式
     *
     * @param url
     * @param params
     * @return
     */
    public synchronized static String doGet(String url, Map<String, Object> params) throws Exception {
        log.debug("doGet to URL=" + url);
        String apiUrl = url;
        StringBuffer param = new StringBuffer();
        int i = 0;
        for (String key : params.keySet()) {
            if (i == 0)
                param.append("?");
            else
                param.append("&");
            param.append(key).append("=").append(params.get(key));
            i++;
        }
        apiUrl += param;
        String result = null;
        HttpClient httpclient = new DefaultHttpClient();
        try {
            HttpGet httpGet = new HttpGet(apiUrl);
            HttpResponse response = httpclient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            log.debug("Return statusCode from remote =" + statusCode);
            if (statusCode != HttpStatus.SC_OK) {
                throw new RemoteServiceException(apiUrl, statusCode, null);
            }
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                InputStream instream = entity.getContent();
                result = IOUtils.toString(instream, "UTF-8");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.debug("Return result =" + result);
        return result;
    }

    /**
     * 发送 GET 请求（HTTP），K-V形式
     *
     * @param url
     * @param params
     * @return
     */
    public synchronized static <T> ResponseMessage<T> doGetForResponseMessage(String url, Map<String, Object> params, TypeReference typeReference) throws CustomException {
        log.debug("doGet to URL=" + url);
        String apiUrl = url;
        StringBuffer param = new StringBuffer();
        ResponseMessage<T> returnObject = null;
        int i = 0;
        for (String key : params.keySet()) {
            if (i == 0)
                param.append("?");
            else
                param.append("&");
            param.append(key).append("=").append(params.get(key));
            i++;
        }
        apiUrl += param;
        HttpClient httpclient = new DefaultHttpClient();
        try {
            HttpGet httpGet = new HttpGet(apiUrl);
            HttpResponse response = httpclient.execute(httpGet);
            int statusCode = response.getStatusLine().getStatusCode();
            log.debug("Return status code =" + response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new RemoteServiceException(url, response.getStatusLine().getStatusCode(), null);
            }
            HttpEntity entity = response.getEntity();
            String httpStr = EntityUtils.toString(entity, "UTF-8");
            try {
                returnObject = JacksonJsonUtil.jsonToBeanByTypeReference(httpStr, typeReference);
            } catch (Exception e) {
                e.printStackTrace();
                throw new CustomException(e);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new CustomException(e);
        }
        return returnObject;
    }

    /**
     * 发送 POST 请求（HTTP），不带输入数据
     *
     * @param apiUrl
     * @return
     */
    public synchronized static String doPost(String apiUrl) throws Exception {
        return doPost(apiUrl, new HashMap<String, Object>());
    }

    /**
     * 发送 POST 请求（HTTP），K-V形式
     *
     * @param url    API接口URL
     * @param params 参数map
     * @return
     */
    public synchronized static String doPost(String url, Map<String, Object> params) throws Exception {
        log.debug("doGet to URL by K-V=" + url);
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response = null;

        try {
            httpPost.setConfig(requestConfig);
            List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
                        .getValue().toString());
                pairList.add(pair);
            }
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
            response = httpClient.execute(httpPost);
            log.debug("Return response info =" + response);
            log.debug("Response content:" + response.toString());
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new RemoteServiceException(url, response.getStatusLine().getStatusCode(), null);
            }
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        log.debug("Return result =" + result);
        return result;
    }

    /**
     * 发送 POST 请求（HTTP），JSON形式
     *
     * @param url
     * @param json json对象
     * @return
     */
    public synchronized static String doPost(String url, Object json) throws Exception {
        log.debug("doGet to URL by Json=" + url);
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String httpStr = null;
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response = null;

        try {
            httpPost.setConfig(requestConfig);
            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");//解决中文乱码问题
            stringEntity.setContentEncoding("UTF-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            response = httpClient.execute(httpPost);
            log.debug("Return status code =" + response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new RemoteServiceException(url, response.getStatusLine().getStatusCode(), null);
            }
            HttpEntity entity = response.getEntity();

            httpStr = EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return httpStr;
    }

    /**
     * 发送 POST 请求（HTTP），JSON形式
     *
     * @param url
     * @param objectToJson  需要转化为Json格式的对象
     * @param typeReference 返回参数转化的类
     * @return
     */
    public synchronized static <T> ResponseMessage<T> doPostForResponseMessage(String url, Object objectToJson, TypeReference typeReference) throws CustomException {
        log.debug("doPost to URL by Json=" + url);
        CloseableHttpClient httpClient;
        if (url.toLowerCase().startsWith(SSL_HTTP_PREFIX)) {
            httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();

        } else {
            httpClient = HttpClients.createDefault();
        }
        HttpPost httpPost = new HttpPost(url);
        CloseableHttpResponse response = null;
        ResponseMessage<T> returnObject = null;
        try {
            httpPost.setConfig(requestConfig);
            StringEntity stringEntity = new StringEntity(JSONObject.toJSONString(objectToJson), "UTF-8");//解决中文乱码问题
            stringEntity.setContentEncoding("UTF-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            response = httpClient.execute(httpPost);
            log.debug("Return status code =" + response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new RemoteServiceException(url, response.getStatusLine().getStatusCode(), null);
            }
            HttpEntity entity = response.getEntity();
            String httpStr = EntityUtils.toString(entity, "UTF-8");
            returnObject = JacksonJsonUtil.jsonToBeanByTypeReference(httpStr, typeReference);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return returnObject;
    }

    /**
     * 发送 SSL POST 请求（HTTPS），K-V形式
     *
     * @param apiUrl API接口URL
     * @param params 参数map
     * @return
     */
    public synchronized static String doPostSSL(String apiUrl, Map<String, Object> params) throws Exception {
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        String httpStr = null;

        try {
            httpPost.setConfig(requestConfig);
            List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
                        .getValue().toString());
                pairList.add(pair);
            }
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("utf-8")));
            response = httpClient.execute(httpPost);
            log.debug("Return status code =" + response.getStatusLine().getStatusCode());

            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new RemoteServiceException(apiUrl, response.getStatusLine().getStatusCode(), null);
            }
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                return null;
            }
            httpStr = EntityUtils.toString(entity, "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return httpStr;
    }

    /**
     * 发送 SSL POST 请求（HTTPS），JSON形式
     *
     * @param apiUrl API接口URL
     * @param json   JSON对象
     * @return
     */
    public synchronized static JSONObject doPostSSL(String apiUrl, Object json) {
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(createSSLConnSocketFactory()).setConnectionManager(connMgr).setDefaultRequestConfig(requestConfig).build();
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        String httpStr = null;

        try {
            httpPost.setConfig(requestConfig);
            StringEntity stringEntity = new StringEntity(json.toString(), "UTF-8");//解决中文乱码问题
            stringEntity.setContentEncoding("UTF-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            response = httpClient.execute(httpPost);
            log.debug("Return status code =" + response.getStatusLine().getStatusCode());
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                throw new RemoteServiceException(apiUrl, response.getStatusLine().getStatusCode(), null);
            }
            HttpEntity entity = response.getEntity();
            if (entity == null) {
                throw new CustomException("企业服务器未返回数据。");
            }
            httpStr = EntityUtils.toString(entity, "utf-8");
            if (StringUtil.isEmpty(httpStr)) {
                log.debug("doPostSSL 企业服务器未返回数据");
                throw new CustomException("企业服务器未返回数据。");
            }
            log.debug("请求成功：resp" + httpStr);
            JSONObject respJson = JSONObject.parseObject(httpStr);
            if (respJson.getInteger("errcode") != 0) {
                log.debug("doPostSSL请求失败，错误代码：" + respJson.getInteger("errcode") + "，错误原因：" + respJson.getString("errmsg"));
                throw new CustomException("请求腾讯服务器错误，错误代码：" + respJson.getInteger("errcode") + "，错误原因：" + respJson.getString("errmsg"));
            }
            return respJson;
        } catch (CustomException e) {
            throw new CustomException(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("请求腾讯服务器失败。");
        } finally {
            if (response != null) {
                try {
                    EntityUtils.consume(response.getEntity());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 创建SSL安全连接
     *
     * @return
     */
    private synchronized static SSLConnectionSocketFactory createSSLConnSocketFactory() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {

                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {

                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }

                @Override
                public void verify(String host, SSLSocket ssl) throws IOException {
                }

                @Override
                public void verify(String host, X509Certificate cert) throws SSLException {
                }

                @Override
                public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
                }
            });
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return sslsf;
    }


    /**
     * 测试方法
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {

        //http://192.168.140.238:8080/FundInfoController/getFundInfo.do
        //fundCode:000066
        //commercialTenantCode:000001
//        JSONObject jo = new JSONObject();
//        jo.put("fundCode", "000066");
//        jo.put("commercialTenantCode", "000001");
//        String returnValue = doPost("http://192.168.140.238:8080/FundInfoContorller/getFundInfo.do", jo);
//        System.out.println(returnValue);


    }

    /**
     * 发送请求
     *
     * @param url 请求的地址
     */
    public static JSONObject doGetSSL(String url) throws CustomException {
        log.info("---doGetSSL---");
        log.info("[url]=" + url);

        CloseableHttpClient httpClient = HttpUtil.createSSLClientDefault();
        HttpGet httpGet = new HttpGet(url);
        CloseableHttpResponse response;
        try {
            response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            String resp = EntityUtils.toString(entity, "UTF-8");
            EntityUtils.consume(entity);
            if (StringUtil.isEmpty(resp)) {
                log.debug("企业服务器未返回数据。");
                throw new CustomException("企业服务器未返回数据。");
            }
            JSONObject respJson = JSONObject.parseObject(resp);
            log.debug("请求成功：resp" + resp);
            if (respJson.getInteger("errcode") != 0) {
                log.debug("请求失败，错误代码：" + respJson.getInteger("errcode") + "，错误原因：" + respJson.getString("errmsg"));
                throw new CustomException("请求失败，错误代码：" + respJson.getInteger("errcode") + "，错误原因：" + respJson.getString("errmsg"));
            }
            return respJson;
        } catch (CustomException e) {
            throw new CustomException(e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            throw new CustomException("数据查询失败了。");
        } finally {
            httpGet.releaseConnection();
        }
    }

    public static CloseableHttpClient createSSLClientDefault() {
        log.info("--HTTPS加密协议SSL安全加固---createSSLClientDefault---");
        try {
            SSLContext sslContext = new SSLContextBuilder().useTLS().loadTrustMaterial(
                    null, new TrustStrategy() {
                        public boolean isTrusted(X509Certificate[] chain,
                                                 String authType) throws CertificateException {
                            return true;
                        }
                    }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslContext);
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        return HttpClients.createDefault();
    }

    public static String reqJsonGet(String url) {
        log.debug("---do get http---");
        CloseableHttpClient httpClient = createSSLClientDefault();
        HttpPost get = new HttpPost(url);
        CloseableHttpResponse response;
        try {
            response = httpClient.execute(get);
            HttpEntity entity = response.getEntity();
            String respStr = EntityUtils.toString(entity, "UTF-8");
            EntityUtils.consume(entity);
            log.debug("do get, respStr=" + respStr);
            return respStr;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            get.releaseConnection();
        }
        return "";
    }

    /**
     * 发送 POST 请求（HTTP），K-V形式
     *
     * @param url
     * @param params
     * @param typeReference
     * @param <T>
     * @return
     * @throws Exception
     */
    public synchronized static <T> ResponseMessage<T> doPostForResponseMessage(String url, Map<String, Object> params, TypeReference typeReference) {
        ResponseMessage<T> returnObject = null;
        try {
            String rs = HttpUtil.doPost(url, params);
            if (rs != null) {
                returnObject = JacksonJsonUtil.jsonToBeanByTypeReference(rs, typeReference);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return returnObject;
    }
}